import { SimulateController } from "./Controller";
import { ADCUICoreNode, ShapeKeyEnum, UIDataType } from "@/constants/types";
import * as remote from "@electron/remote";
import { treeForeach } from '@/util/common';
import { IpcUpdateUIFrameRenderData, UiGetInitData } from "@/engine/types/SimUI";
import { UIOpenNotificationData } from "./types/Socket";
import { SubWinName } from "./types/Win";
import { RouterName } from "@/views/router";
import { ImageInfo, WinPraram } from "./types/ImageSwitcher";
import { PropertyKey } from "@/constants/constants";

export class SimUI {

  controller: SimulateController;
  instanceId = '';
  uiId = '';
  projectId = '';
  sessionId = '';
  simUIData: ADCUICoreNode[] = [];
  uiGetInitData: UiGetInitData[] = []
  simUIDataMap = new Map<string, ADCUICoreNode[]>()
  objectIdToUIPropertyId = new Map<string, string[]>()
  baseObjectId = ''
  pannelImageSwitcherMap = new Map<string, ImageInfo[]>()
  winparams = {} as {[key: string]: string}

  constructor(c: SimulateController) {
    this.controller = c;
  }

  private createObjectIdToUIPropertyIdMap(p: UiGetInitData[]) {
    for (const uiData of p) {
      if (uiData.properties && uiData.properties.length) {
        let propertiesRecord = [];
        let propertiesObjectRecord = [];
        for (const property of uiData.properties) {
          if (property.valueType !== 'Object') {
            propertiesRecord.push(property.propertyId);
          } else {
            propertiesObjectRecord.push(property);
          }
        }
        // not-object
        let objectIdToUIPropertyIdRecord = this.objectIdToUIPropertyId.get(uiData.objectId);
        if (!objectIdToUIPropertyIdRecord) {
          objectIdToUIPropertyIdRecord = propertiesRecord;
        } else {
          objectIdToUIPropertyIdRecord.push(...propertiesRecord);
        }
        this.objectIdToUIPropertyId.set(uiData.objectId, objectIdToUIPropertyIdRecord);

        // object
        for (const objectProperty of propertiesObjectRecord) {
          if (objectProperty.values && objectProperty.values.length) {
            for (const pv of objectProperty.values) {
              objectIdToUIPropertyIdRecord = this.objectIdToUIPropertyId.get(pv);
              if (objectIdToUIPropertyIdRecord) {
                objectIdToUIPropertyIdRecord.push(objectProperty.propertyId);
              } else {
                objectIdToUIPropertyIdRecord = [objectProperty.propertyId];
              }
              this.objectIdToUIPropertyId.set(pv, objectIdToUIPropertyIdRecord);
            }
          }
        }
      }
    }
  }

  async subWinOpen(n: UIOpenNotificationData) {
    await this.controller.winService.openPluginSubWin(
      RouterName.FrameUi,
      { width: 1000, height: 600, skipTaskbar: true, title: SubWinName.UIFrame },
      { instanceId: n.instanceId, uiId: n.uiId, projectId: this.controller.projectId, sessionId: this.controller.sessionId }
    );
  }

  async ipcUpdateSimUIData(data: IpcUpdateUIFrameRenderData) {
    const changingUIDatas = this.simUIDataMap.get(`${data.objectId}-${data.propertyId}`);
    if (changingUIDatas) {
      for (const uiData of changingUIDatas) {
        uiData.propertyData.value = data.valuse[0].value;
      }
    }
  }

  async frameInit() {
    this.winparams = this.controller.winService.getQueryParams();
    this.instanceId = this.winparams?.instanceId;
    this.uiId = this.winparams?.uiId;
    this.projectId = this.winparams?.projectId;
    this.sessionId = this.winparams?.sessionId;

    this.uiGetInitData = (await this.controller.simApi.uiGetInitData({
      objectId: this.instanceId,
      projectId: this.uiId,
      simulationSessionId: this.sessionId,
    })).data?.data;

    if (this.uiGetInitData) {
      this.baseObjectId = this.uiGetInitData[0].objectId;
      this.createObjectIdToUIPropertyIdMap(this.uiGetInitData);
      console.info("uiGetInitData: ", this.uiGetInitData);
    }

    const shapeQueryCuieShapeTreeData = (await this.controller.modelApi.shapeQueryCuieShapeTree({
      projectId: this.projectId,
      modelId: this.uiId,
    })).data?.data;

    if (shapeQueryCuieShapeTreeData) {
      this.simUIData.push(...shapeQueryCuieShapeTreeData);
      const panelModelIds: string[] = [];
      treeForeach(shapeQueryCuieShapeTreeData, async (t: ADCUICoreNode) => {
        
        if (t.shapeKey === ShapeKeyEnum.SimulationPanel) {
          // record ids to deal imageswitcher infos
          panelModelIds.push(t.modelId);
        }
        // get ObjectId
        let gotObjectId = '';
        const iteratorUiPropertyidArr: string[] = [];
        if (t.propertyPath) { // has path
          const pathArr = t.propertyPath.split('->').filter(p => p);
          iteratorUiPropertyidArr.push(...pathArr);
        } else {
          iteratorUiPropertyidArr.push(t.propertyId);
        }
        let breakFlag = false;
        for (let i = iteratorUiPropertyidArr.length - 1; i >= 0; i--) {
          for (const [k, v] of this.objectIdToUIPropertyId.entries()) {
            if (v.includes(iteratorUiPropertyidArr[i])) {
              gotObjectId = k;
              breakFlag = true;
              break;
            }
          }
          if (breakFlag) {
            break;
          }
        }

        const gotUiDataValueAndType = this.getUiDataValueAndType(this.uiGetInitData, t.propertyId, gotObjectId);
        // add[objectId|value|valueType]
        t.objectId = gotObjectId || this.baseObjectId;
        t.propertyData.value = gotUiDataValueAndType?.values[0] as string;
        t.valueType = gotUiDataValueAndType?.valueType as UIDataType;
        console.log(t.valueType, "t.valueType", gotUiDataValueAndType);
        // if (t.valueType === UIDataType.Enumeration && t.propertyData.value) {
        //   t.propertyData.valueModelId = gotUiDataValueAndType?.values[0];
        // }
        // the same project model can add UI repeatedly, which has same scope
        let simUIDataMapGet = this.simUIDataMap.get(`${t.objectId}-${t.propertyId}`);
        if (simUIDataMapGet) {
          simUIDataMapGet.push(t);
        } else {
          simUIDataMapGet = [t];
        }
        this.simUIDataMap.set(`${t.objectId}-${t.propertyId}`, simUIDataMapGet);
      });
      console.info('ui data: ', this.simUIData);
      await this.fillPannelImageSwitcherMap(panelModelIds);
    }
    remote.getCurrentWindow().setSize(this.simUIData[0].style.width, this.simUIData[0].style.height + 40);
  }

  getUiDataValueAndType(data: UiGetInitData[], propertyId: string, objectId: string) {
    if (!data || !data.length) return;
    for (const d of data) {
      if (d.objectId === objectId) {
        for (const p of d.properties) {
          if (p.propertyId === propertyId) {
            return p;
          }
        }
      }
    }
  }

  uiDataChangeNoteBackend(objectId: string, propertyId: string, values: any[]) {
    this.controller.simApi.uiUpdatePropertyValue({
      objectId,
      propertyId,
      simulationSessionId: this.sessionId,
      values
    });
  }

  /**
   * 
   * @param propertyId ui property id
   * @param contextId 上下文id
   * @param modelId 当前UI节点的模型id
   */
  uiButtonClick(propertyId:string, contextId: string, modelId: string) {
    this.controller.simApi.uiClickButton({
      elementId: propertyId,
      simulationSessionId: this.sessionId,
      contextId,
      modelId,
    });
  }

  async fillPannelImageSwitcherMap(ids: string[]) {
    for (const modelId of ids) {
      const propertyValuses = (await this.controller.modelApi.modelPropertyListByKeys({
        projectId: this.winparams.projectId,
        modelId: modelId,
        keys: [PropertyKey.NestedUIConfig_Config]
      })).data?.data;
      if (propertyValuses[0]?.value) {
        const imageSwitcherModel = (await this.controller.modelApi.modelPropertyListByKeys({
          projectId: this.winparams.projectId,
          modelId: propertyValuses[0].value.id,
          keys: [PropertyKey.Class_ownedAttribute],
        })).data?.data;
        if (imageSwitcherModel[0]?.value.length) {
          const imageInfos = await this.controller.imageSwitcherService.dealImageInfo(
            imageSwitcherModel[0]?.value,
            [PropertyKey.ActiveImage_ActiveElement, PropertyKey.ActiveImage_OnClick],
            this.winparams as WinPraram
          );
          console.log(imageInfos, "imageInfos");
          this.pannelImageSwitcherMap.set(modelId, imageInfos);
        }
      }
    }
    this.bindBgToSimUIData();
  }
  bindBgToSimUIData() {
    treeForeach(this.simUIData, (t: ADCUICoreNode) => {
      if (t.shapeKey === ShapeKeyEnum.SimulationPanel) {
        const imageInfos = this.pannelImageSwitcherMap.get(t.modelId);
        if (imageInfos) {
          t.imageInfo = imageInfos[0];
        }
      }
    });
  }
  changeSimUIDataBg(modelId: string) {
    for (const [k, v] of this.pannelImageSwitcherMap.entries()) {
      for (const image of v) {
        if (image.statusId === modelId) {
          treeForeach(this.simUIData, (t: ADCUICoreNode) => {
            if (t.modelId === k) {
              t.imageInfo = image;
            }
          });
          break;
        }
      }
    }
  }

  getBaseStyle(shape: ADCUICoreNode) {
    const { color, fontSize, fontWeight, fontFamily, background, border, textAlign, fontStyle } = shape.style;
    return {
      color: color,
      fontSize: fontSize + 'px',
      fontWeight: fontWeight,
      fontFamily: fontFamily,
      background: background,
      border: border,
      textAlign: textAlign as any,
      fontStyle: fontStyle || 'normal',
    };
  }
}
